Customising Filter and Sort Modal
This doc is for showing how to customize sort and filter modal
- Register the block in the blocks/index.js
import CollectionSortFilter from './components/CollectionSortFilter/index.js';
const blocks = [
// other blocks
{
name: 'shopify/collection-filter',
View: CollectionSortFilter,
},
];
export { blocks };
- Inside CollectionSortFilter/index.js, add this code
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { ShopifyFilterProviderWrapper } from '@appmaker-xyz/shopify';
import FilterModal from './filter/FilterModal';
import FilterButton from './filter/components/FilterButton';
import SortButton from '../sort/components/SortButton';
import SortModal from '../sort/SortModal';
export default function Filter(props) {
const { attributes } = props;
const { filterButtonStyle, sortButtonStyle } = attributes;
return (
<ShopifyFilterProviderWrapper {...props}>
<View style={styles.container}>
<FilterButton
attributes={{
customStyles: filterButtonStyle,
}}
/>
<View style={styles.separator} />
<SortButton
attributes={{
customStyles: sortButtonStyle,
}}
/>
</View>
<SortModal />
<FilterModal />
</ShopifyFilterProviderWrapper>
);
}
- Inside filter/FilterModal.js, add this code
import {
View,
Text,
StyleSheet,
Pressable,
FlatList,
SafeAreaView,
ActivityIndicator,
} from 'react-native';
import React from 'react';
import Modal from 'react-native-modal';
import Icon from '@appmaker-xyz/uikit/Icons/Feather';
import {
useFilterOptions,
useCollectionFilter,
useFilterModal,
useSelectedFiltersCount,
useIsAnyFilterSelected,
} from '@appmaker-xyz/shopify';
import PriceRange from './components/PriceRange';
import FilterListOption from './components/FilterListOption';
import FilterColorSwatch from './components/FilterColorSwatch';
import { AppmakerText } from '@appmaker-xyz/uikit';
import { appmaker } from '@appmaker-xyz/core';
const FilterOptions = {
LIST: FilterListOption,
PRICE_RANGE: PriceRange,
COLOR_SWATCH: FilterColorSwatch,
HIDDEN: () => null,
};
function FilterItemContent({ item }) {
const { selectFilter, removeFilter } = useFilterOptions();
const { label, type } = item;
const FilterComponent = FilterOptions[type];
return (
<View style={styles.itemContainer}>
{FilterComponent ? (
<FilterComponent
item={item}
selectFilter={selectFilter}
removeFilter={removeFilter}
filterKey={item.id}
/>
) : null}
</View>
);
}
function ModalHeader(props) {
const { clearSelectedFilters } = useFilterOptions();
const isFilterSelected = useIsAnyFilterSelected();
return (
<View style={styles.topBar}>
<Pressable style={styles.back} onPress={props.onClose}>
<Icon name="x" size={20} color="#000" />
</Pressable>
<View style={styles.filtersHeadTextContainer}>
<AppmakerText style={styles.filtersHeadText}>Filters</AppmakerText>
{props.loading ? <ActivityIndicator size={16} color="#1B1B1B" /> : null}
</View>
{isFilterSelected ? (
<Pressable
onPress={clearSelectedFilters}
style={styles.removeAllButton}>
<AppmakerText style={styles.clearAllText}>Clear All</AppmakerText>
</Pressable>
) : null}
</View>
);
}
function FilterItem({ item, selectTab, isCurrentTab }) {
const filtersCount = useSelectedFiltersCount(item.id);
const currentTabStyle = isCurrentTab ? styles.currentTab : {};
const label = item?.upper_case ? item.label.toUpperCase() : item.label;
const isDisabled = item?.empty === true;
let textColor = {
color: 'black',
};
if (isDisabled) {
textColor = {
color: '#A1A1AA',
};
}
return (
<Pressable onPress={() => selectTab(item.id)}>
<View style={[styles.tabBarStyle, currentTabStyle]}>
<Text style={{ ...styles.tabText, ...textColor }}>{label}</Text>
{filtersCount > 0 ? (
<Text style={styles.activeCountText}>{filtersCount}</Text>
) : null}
</View>
</Pressable>
);
}
function FilterContent({ avilableFilters }) {
const [currentTabId, selectTabId] = React.useState(avilableFilters[0]?.id);
const tabIndexMap = React.useMemo(() => {
const map = {};
avilableFilters.forEach((item, index) => {
map[item.id] = index;
});
return map;
}, [avilableFilters]);
function renderItem({ item }) {
return (
<FilterItem
item={item}
selectTab={selectTabId}
isCurrentTab={item?.id === currentTabId}
/>
);
}
const activeFilters = avilableFilters[tabIndexMap[currentTabId]];
return (
<View style={styles.filterView}>
<View style={styles.filterListingView}>
<FlatList
data={avilableFilters}
renderItem={renderItem}
contentContainerStyle={styles.containerStyle}
keyExtractor={(item, index) => index.toString()}
/>
</View>
<View style={styles.filterContentView}>
{activeFilters ? <FilterItemContent item={activeFilters} /> : null}
</View>
</View>
);
}
export default function FilterModal() {
const { avilableFilters, applyFilters, isNextFiltersLoading } =
useCollectionFilter();
const { isFilterModalShown, closeFilterModal } = useFilterModal();
return (
<View style={styles.mainContainer}>
<Modal
isVisible={isFilterModalShown}
style={styles.modal}
onDismiss={closeFilterModal}
onRequestClose={closeFilterModal}>
<SafeAreaView style={styles.modalContainer}>
<ModalHeader
onClose={closeFilterModal}
loading={isNextFiltersLoading}
/>
{avilableFilters?.length > 0 ? (
<>
<FilterContent avilableFilters={avilableFilters} />
<View style={styles.applyButtonContainer}>
<Pressable
onPress={() => {
applyFilters();
closeFilterModal();
}}
style={styles.applyButton}>
<AppmakerText style={styles.applyButtonText}>
Apply Filters
</AppmakerText>
</Pressable>
</View>
</>
) : null}
</SafeAreaView>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
mainContainer: {
// flex: 1,
// backgroundColor: 'red',
margin: 0,
padding: 0,
},
modal: {
margin: 0,
backgroundColor: '#f2f2f2',
},
modalContainer: {
backgroundColor: '#fff',
flex: 1,
margin: 0,
padding: 0,
width: '100%',
},
topBar: {
flexDirection: 'row',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingVertical: 12,
borderBottomColor: '#e2e2e2',
borderBottomWidth: 0.5,
},
back: {
padding: 6,
backgroundColor: '#f2f2f2',
borderRadius: 18,
marginRight: 12,
},
removeAllButton: {
paddingVertical: 6,
},
clearAllText: {
color: '#FF0000',
},
filtersHeadTextContainer: {
flexDirection: 'row',
alignItems: 'center',
flexGrow: 1,
},
filtersHeadText: {
fontFamily: 'NunitoSans-Bold',
color: '#1B1B1B',
marginRight: 12,
},
tabBarStyle: {
flexGrow: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 8,
paddingHorizontal: 10,
marginVertical: 1,
},
currentTab: {
backgroundColor: '#fff',
},
tabText: { flexShrink: 1 },
activeCountText: {
color: '#64748B',
padding: 2,
borderRadius: 4,
marginLeft: 10,
fontSize: 12,
},
filterView: {
flex: 1,
backgroundColor: 'white',
flexDirection: 'row',
},
filterListingView: {
flex: 2,
backgroundColor: '#F8F6F2',
},
filterContentView: {
flex: 3,
},
applyButtonContainer: {
padding: 10,
backgroundColor: '#fff',
borderTopColor: '#E9EDF1',
borderTopWidth: 1,
},
applyButton: {
backgroundColor: '#000',
paddingVertical: 12,
borderRadius: 8,
},
applyButtonText: {
color: '#fff',
fontSize: 16,
textAlign: 'center',
},
});
- Inside filter/components/FilterButton.js, add this code
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { useFilterModal, useIsAnyFilterSelected } from '@appmaker-xyz/shopify';
import { AppmakerText } from '@appmaker-xyz/uikit';
import FilterIcon from '../assets/FilterIcon';
export function FilterButton({ attributes: { customStyles = {} } = {} }) {
const {
activeColor,
bgColor,
textColor,
outline,
noIcon,
small,
wholeContainerStyle,
} = customStyles;
const { openFilterModal } = useFilterModal();
const isFilterSelected = useIsAnyFilterSelected();
const containerStyles = [styles.filterButton];
const activeContainerStyles = [styles.activeContainer];
if (bgColor) {
containerStyles.push({ backgroundColor: bgColor });
}
if (outline) {
containerStyles.push({ borderWidth: 1, borderColor: outline });
}
if (noIcon) {
activeContainerStyles.push({ marginLeft: 0 });
}
if (small) {
containerStyles.push({ paddingVertical: 8 });
}
if (wholeContainerStyle) {
containerStyles.push(wholeContainerStyle);
}
return (
<Pressable onPress={openFilterModal} style={containerStyles}>
{noIcon ? null : <FilterIcon color={textColor} />}
<View style={activeContainerStyles}>
{isFilterSelected ? (
<View
style={[
styles.activeDot,
{ backgroundColor: activeColor || '#ff0000' },
]}
/>
) : null}
<AppmakerText
style={styles.filterButtonText}
fontColor={textColor ? textColor : '#fff'}>
Filter
</AppmakerText>
</View>
</Pressable>
);
}
const styles = StyleSheet.create({
filterButton: {
backgroundColor: '#000',
paddingVertical: 12,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
filterButtonText: {
textAlign: 'center',
fontSize: 16,
},
activeContainer: {
position: 'relative',
marginLeft: 6,
},
activeDot: {
position: 'absolute',
zIndex: 1,
top: 2,
right: -16,
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
});
export default FilterButton;
- Inside filter/components/FilterColorSwatch.js, add this code
import React from 'react';
import { CheckBox } from '@appmaker-xyz/ui';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
import { FlatList, StyleSheet, Text, Image, View } from 'react-native';
function ItemLabel({ item, count, isUpperCase, label }) {
if (item?.displayType === 'IMAGE' && item?.image) {
return (
<View style={styles.imageContainer}>
<Image
source={{ uri: item.image }}
style={styles.colorBox}
resizeMode="cover"
/>
<Text style={styles.activeCountText}>({count})</Text>
</View>
);
} else if (item?.displayType === 'COLOR_CODE' && item?.color_code) {
return (
<View style={styles.imageContainer}>
<View style={[styles.colorBox, { backgroundColor: item.color_code }]} />
<Text style={styles.activeCountText}>({count})</Text>
</View>
);
} else {
return (
<View style={styles.imageContainer}>
<Text
style={
isUpperCase ? styles.checkBoxItemCaptialize : styles.checkBoxItem
}>
{label}
</Text>
<Text style={styles.activeCountText}>({count})</Text>
</View>
);
}
}
function CheckBoxItem({
label,
count,
selectFilter,
removeFilter,
filterKey,
item,
parentItem,
}) {
const selectedItem = useSelectedFilterItem({
filterKey,
filterValueId: item.id,
});
const isUpperCase = parentItem?.upper_case || false;
return (
<CheckBox
activeColor={'#000'}
small={true}
label={
<ItemLabel
item={item}
count={count}
isUpperCase={isUpperCase}
label={label}
/>
}
value={selectedItem?.id === item.id}
onValueChange={(status) => {
if (status) {
selectFilter(filterKey, item.id, item);
} else {
removeFilter(filterKey, item.id, item);
}
}}
/>
);
}
function FilterColorSwatch({ item, selectFilter, removeFilter }) {
const { values } = item;
function renderItem({ item: filterItem }) {
return (
<CheckBoxItem
label={filterItem.label}
count={filterItem.count}
selectFilter={selectFilter}
removeFilter={removeFilter}
filterKey={item.id}
item={filterItem}
parentItem={item}
/>
);
}
return (
<FlatList
data={values}
renderItem={renderItem}
contentContainerStyle={styles.filterContentViewContainer}
keyExtractor={(item, index) => index.toString()}
/>
);
}
const styles = StyleSheet.create({
activeCountText: {
color: '#64748B',
padding: 2,
borderRadius: 4,
marginLeft: 6,
fontSize: 12,
},
filterContentViewContainer: {
padding: 10,
},
checkBoxItemCaptialize: {
textTransform: 'capitalize',
},
checkBoxItem: {},
imageContainer: {
flexDirection: 'row',
alignItems: 'center',
},
colorBox: {
width: 50,
height: 22,
borderRadius: 25,
overflow: 'hidden',
},
});
export default FilterColorSwatch;
- Inside filter/components/FilterListOption.js, add this code
import React from 'react';
import { CheckBox } from '@appmaker-xyz/ui';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
import { FlatList, StyleSheet, Text } from 'react-native';
function CheckBoxItem({
label,
count,
selectFilter,
removeFilter,
filterKey,
item,
parentItem,
}) {
const isEmpty = item?.empty === true;
const selectedItem = useSelectedFilterItem({
filterKey,
filterValueId: item.id,
});
const isUpperCase = parentItem?.upper_case || false;
let colorStyle = {
color: 'black',
};
if (isEmpty) {
colorStyle = {
color: '#A1A1AA',
};
}
return (
<CheckBox
activeColor={'#000'}
small={true}
disable={isEmpty}
label={
<>
<Text
style={
(isUpperCase
? styles.checkBoxItemCaptialize
: styles.checkBoxItem,
colorStyle)
}>
{label}
</Text>
<Text style={styles.activeCountText}>({count})</Text>
</>
}
value={selectedItem?.id === item.id}
onValueChange={(status) => {
if (status) {
selectFilter(filterKey, item.id, item);
} else {
removeFilter(filterKey, item.id, item);
}
}}
/>
);
}
function FilterListOption({ item, selectFilter, removeFilter }) {
const { values } = item;
function renderItem({ item: filterItem }) {
return (
<CheckBoxItem
label={filterItem.label}
count={filterItem.count}
selectFilter={selectFilter}
removeFilter={removeFilter}
filterKey={item.id}
item={filterItem}
parentItem={item}
/>
);
}
return (
<FlatList
data={values}
renderItem={renderItem}
contentContainerStyle={styles.filterContentViewContainer}
keyExtractor={(item, index) => index.toString()}
/>
);
}
const styles = StyleSheet.create({
activeCountText: {
color: '#64748B',
padding: 2,
borderRadius: 4,
marginLeft: 10,
fontSize: 12,
},
filterContentViewContainer: {
padding: 10,
},
checkBoxItemCaptialize: {
textTransform: 'capitalize',
},
checkBoxItem: {},
});
export default FilterListOption;
- Inside filter/components/PriceRange.js, add this code
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MultiSlider from '@ptomasroos/react-native-multi-slider';
import { styles as stylesConfig } from '@appmaker-xyz/uikit';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
const { spacing, color } = stylesConfig;
function getMinMax(item) {
let min = 0;
let max = 10000;
try {
const config = JSON.parse(item?.values[0].input);
min = config.price.min;
max = config.price.max;
} catch (error) {}
return {
min,
max,
availableMin: item?.values?.[0]?.availableMin,
availableMax: item?.values?.[0]?.availableMax,
};
}
function CustomLabel(props) {
return (
<View style={styles.customLabelContainer}>
<Text style={styles.customLabelText}>{props.oneMarkerValue}</Text>
<Text style={styles.customLabelText}>{props.twoMarkerValue}</Text>
</View>
);
}
function PriceRange({ item, filterKey, selectFilter }) {
const filterValueId = 'price_range';
const selectedItem = useSelectedFilterItem({
filterKey,
filterValueId: filterKey,
});
const { min, max, availableMin, availableMax } = getMinMax(item);
if (min === max || min > max) {
return null;
}
return (
<View style={styles.sliderContainer}>
{/* <Text>{JSON.stringify(item, null, 2)}</Text> */}
<MultiSlider
values={[selectedItem?.min || min, selectedItem?.max || max]}
sliderLength={180}
enableLabel={true}
customLabel={CustomLabel}
isMarkersSeparated={true}
trackStyle={styles.trackStyle}
selectedStyle={styles.slideSelectedStyle}
markerStyle={styles.markerStyle}
pressedMarkerStyle={styles.markerPressed}
min={availableMin || min}
max={availableMax || max}
onValuesChangeFinish={(values) => {
const [minValue, maxValue] = values;
selectFilter(filterKey, filterKey, {
type: 'PRICE_RANGE',
min: minValue,
max: maxValue,
});
}}
/>
</View>
);
}
const styles = StyleSheet.create({
trackStyle: {
borderRadius: spacing.small,
height: 2,
},
slideSelectedStyle: {
backgroundColor: color.dark,
},
markerStyle: {
height: 20,
width: 20,
borderRadius: spacing.md,
backgroundColor: color.dark,
borderWidth: 0.5,
borderColor: 'transparent',
},
markerPressed: {
backgroundColor: color.demiDark,
},
sliderContainer: {
alignItems: 'center',
paddingVertical: 2,
},
customLabelContainer: {
position: 'relative',
justifyContent: 'space-between',
flexDirection: 'row',
width: 190,
left: -4,
top: 6,
},
customLabelText: {
color: 'black',
fontSize: 12,
},
});
export default PriceRange;
- Inside filter/assets/FilterIcon.js, add this code
import * as React from 'react';
import Svg, { Path } from 'react-native-svg';
const FilterIcon = (props) => (
<Svg
width={16}
height={16}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path
d="M11.217 7.277a2.286 2.286 0 0 0 2.223-1.714h1.989a.571.571 0 0 0 0-1.143H13.44a2.285 2.285 0 0 0-4.446 0H.571a.571.571 0 1 0 0 1.143h8.423a2.286 2.286 0 0 0 2.223 1.714Zm-1.143-2.286a1.143 1.143 0 1 1 1.143 1.143 1.143 1.143 0 0 1-1.16-1.143h.017ZM4.286 12.99a2.285 2.285 0 0 0 2.223-1.714h8.92a.571.571 0 0 0 0-1.143h-8.92a2.286 2.286 0 0 0-4.446 0H.57a.571.571 0 1 0 0 1.143h1.492a2.286 2.286 0 0 0 2.223 1.715Zm-1.143-2.285a1.143 1.143 0 1 1 1.143 1.143 1.143 1.143 0 0 1-1.16-1.143h.017Z"
fill={props.color || '#fff'}
/>
</Svg>
);
export default FilterIcon;
- Inside sort/components/SortButton.js, add this code
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import {
useIsAnySortSelected,
useSortModal,
} from '@appmaker-xyz/shopify';
import { AppmakerText } from '@appmaker-xyz/uikit';
import SortIcon from '../assets/SortIcon';
export function SortButton({ attributes: { customStyles = {} } = {} }) {
const {
activeColor,
bgColor,
textColor,
outline,
noIcon,
small,
wholeContainerStyle,
} = customStyles;
const { openSortModal } = useSortModal();
const isSortApplied = useIsAnySortSelected();
const containerStyles = [styles.sortButton];
const activeContainerStyles = [styles.activeContainer];
if (bgColor) {
containerStyles.push({ backgroundColor: bgColor });
}
if (outline) {
containerStyles.push({ borderWidth: 1, borderColor: outline });
}
if (noIcon) {
activeContainerStyles.push({ marginLeft: 0 });
}
if (small) {
containerStyles.push({ paddingVertical: 8 });
}
if (wholeContainerStyle) {
containerStyles.push(wholeContainerStyle);
}
return (
<Pressable onPress={openSortModal} style={containerStyles}>
{noIcon ? null : <SortIcon color={textColor} />}
<View style={activeContainerStyles}>
{isSortApplied ? (
<View
style={[
styles.activeDot,
{ backgroundColor: activeColor || '#ff0000' },
]}
/>
) : null}
<AppmakerText
style={styles.sortButtonText}
fontColor={textColor ? textColor : '#fff'}>
Sort
</AppmakerText>
</View>
</Pressable>
);
}
const styles = StyleSheet.create({
sortButton: {
flex: 1,
backgroundColor: '#000',
paddingVertical: 12,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
sortButtonText: {
textAlign: 'center',
fontSize: 16,
},
activeContainer: {
position: 'relative',
marginLeft: 6,
},
activeDot: {
position: 'absolute',
zIndex: 1,
top: 2,
right: -16,
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
});
export default SortButton;
- Inside sort/SortModal.js, add this code
import { View, Text, StyleSheet, Pressable } from 'react-native';
import React from 'react';
import Modal from 'react-native-modal';
import Icon from '@appmaker-xyz/uikit/Icons/Feather';
import Radio from './components/Radio';
import {
useSortModal,
useSortValue,
} from '@appmaker-xyz/shopify';
import { appmaker, appPluginStoreApi } from '@appmaker-xyz/core';
import { useTranslation } from 'react-i18next';
import { AppmakerText } from '@appmaker-xyz/uikit';
function ModalHeader(props) {
return (
<View style={styles.topBar}>
<AppmakerText style={styles.sortHeadText}>Sort By</AppmakerText>
<Pressable style={styles.back} onPress={props.onClose}>
<Icon name="x" size={20} color="#000" />
</Pressable>
</View>
);
}
export default function SortModal() {
const { t } = useTranslation();
const sortOptions = [
{
label: t('Default'),
value: ' ',
},
{
label: t('Featured'),
value: 'reverse: false, sortKey: RELEVANCE',
},
{
label: t('Price low to high'),
value: 'reverse: false, sortKey: PRICE',
},
{
label: t('Price high to low'),
value: 'reverse: true, sortKey: PRICE',
},
{
label: t('Best Selling'),
value: 'reverse: false, sortKey: BEST_SELLING',
},
{
label: t('Date, Old To New'),
value: 'reverse: false, sortKey: CREATED',
},
{
label: t('Date, New To Old'),
value: 'reverse: true, sortKey: CREATED',
},
];
const { isSortModalShown, closeSortModal, context } = useSortModal();
let finalSortOptions = appmaker.applyFilters(
'shopify-custom-sort-options',
sortOptions,
context,
);
const pluginSortOptions =
appPluginStoreApi().getState().plugins['advanced-filter-customization']
?.settings?.sort_mappings || [];
finalSortOptions =
pluginSortOptions?.length > 0
? pluginSortOptions
: finalSortOptions || sortOptions;
// const [show, setShow] = React.useState(true);
const { applySort, selectedSort } = useSortValue();
const selectedIndex =
selectedSort && typeof selectedSort === 'string'
? finalSortOptions.findIndex((item) => item.value === selectedSort)
: selectedSort && selectedSort?.id
? finalSortOptions.findIndex(
(item) => item.value?.id === selectedSort?.id,
)
: 0;
return (
<View>
<Modal
isVisible={isSortModalShown}
style={styles.modal}
onDismiss={closeSortModal}
onRequestClose={closeSortModal}>
<View style={styles.modalContainer}>
<ModalHeader onClose={closeSortModal} />
<View style={styles.sortOptions}>
<Radio
color="#000000"
options={finalSortOptions}
selectedSort={selectedSort}
initial={selectedIndex}
onPress={(value) => {
applySort(value);
}}
/>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
modal: {
margin: 0,
justifyContent: 'flex-end',
},
modalContainer: {
backgroundColor: '#fff',
borderTopLeftRadius: 18,
borderTopRightRadius: 18,
overflow: 'hidden',
},
topBar: {
flexDirection: 'row',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingVertical: 8,
borderBottomColor: '#e2e2e2',
borderBottomWidth: 0.5,
},
sortHeadText: {
fontFamily: 'NunitoSans-Bold',
color: '#1B1B1B',
flexGrow: 1,
},
back: {
padding: 6,
borderRadius: 18,
},
sortOptions: {
padding: 12,
},
});
- Inside sort/components/Radio.js, add this code
import React from 'react';
import { useState } from 'react';
import { View, StyleSheet, Text, Pressable } from 'react-native';
const RadioItem = (props) => {
const { label, value, color, activeSort } = props;
const isActive =
activeSort === value || (activeSort?.id && activeSort?.id === value?.id);
const handlePress = () => {
props?.onPress && props?.onPress(value);
};
const themeColor = color || '#000000';
const borderStyle = {
borderColor: isActive ? themeColor : `${themeColor}40`,
};
const activeBg = { backgroundColor: themeColor };
return (
<Pressable style={styles.radioItem} onPress={handlePress}>
<View style={[styles.radioCircle, borderStyle]}>
{isActive ? <View style={[styles.selectedRb, activeBg]} /> : null}
</View>
<Text style={styles.radioText}>{label}</Text>
</Pressable>
);
};
const Radio = (props) => {
const { color, options, onPress, initial, selectedSort } = props;
const [activeSort, setActiveSort] = useState(
selectedSort || options?.[0]?.value || '',
);
const onSelect = (value) => {
setActiveSort(value);
onPress && onPress(value);
};
if (options?.length && options?.length > 0) {
return options.map((item, id) => {
return (
<RadioItem
key={item.id || id}
activeSort={activeSort}
{...item}
color={color}
onPress={onSelect}
/>
);
});
}
return null;
};
const styles = StyleSheet.create({
radioItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 8,
paddingVertical: 6,
},
radioCircle: {
height: 20,
width: 20,
borderRadius: 100,
borderWidth: 1.6,
alignItems: 'center',
justifyContent: 'center',
},
selectedRb: {
width: 12,
height: 12,
borderRadius: 50,
},
radioText: {
marginLeft: 8,
fontSize: 16,
},
});
export default Radio;
- Inside sort/assets/SortIcon.js, add this code
import React from 'react';
import Svg, { Path } from 'react-native-svg';
const SortIcon = (props) => (
<Svg
width={16}
height={16}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path
d="m7.26 11.704-2.963 2.963-2.963-2.963m2.963 2.963v-8.89m10.37-1.48-2.963-2.964-2.963 2.963m2.963 5.926V1.333"
stroke={props.color || '#fff'}
strokeLinecap="round"
strokeLinejoin="round"
/>
</Svg>
);
export default SortIcon;